跳到主要内容

3.2-泛型

Create by fall on 23 Nov 2021 Recently revised in 21 Nov 2024

泛型

最初学习的泛型

function random<T>(arg:T):T{
return arg
}
// 首先是 <T> 在调用函数时使用,T 将会被传入的类型取代。
random<string>('22')
random<number>(333)
// 就像传入参数一样,传入类型,然后调用类型

一般来讲会有几个约定俗成的名称

  • K:顾名思义,对象中的键
  • V:顾名思义,对象中的值
  • T:一般的类型 Type
  • E:元素类型 Element

可以进行省略类型的填写

funtion random<T,U>(key:T,value:U){
console.log(key)
return value
}
random(13,'哇塞')

类型约束

如果想打印一些属性,如果完全不进行约束就会报错,

function trace<T>(arg: T): T {
console.log(arg.size); // Error
return arg;
}
// 可以通过接口去限制该属性
interface Opti{
size:number
}
// 继承该接口之后就不会出现问题
function trac2<T extends Opti>(arg:number):T{
console.log(arg)
return arg
}
// 当然,传入参数时也要传入实现该接口的参数。

泛型工具类型

typeof

主要作用是去获取变量或者是属性的类型

interface Person{
name:string;
age:number
}
const sai:Person = {name:'琦玉',age:23}
type Sai = typeof sai // 此时 type Sai == Person
// 也就可以使用 Sai
const bald:Sai = {name:'秃头披风',age:28}

甚至是函数类型

function toArr(x:number):Array<number>{
return [x]
}
type Rebuild = typeof toArray // (x:number)=>number[]

keyof

TypeScript 2.1 版本引入的,可以获取某种类型的所有件,返回类型是联合类型。

interface Person {
name: string;
age: number;
}

type K1 = keyof Person; // "name" | "age"
type K2 = keyof Person[]; // "length" | "toString" | "pop" | "push" | "concat" | "join"
type K3 = keyof { [x: string]: Person }; // string | number

数字索引,字符串索引

in

用来遍历枚举类型

type Kings = '朱元璋'|'朱棣'|'朱厚照'
type Obj = {
[p in Kings]:any
}
// 此时 type Obj 就是
type Obj = {
朱元璋: any;
朱棣: any;
朱厚照: any;
}

key 和 in 连起来?!

interface Person {
name: string;
age: number;
}
type Man = {
[p in keyof Person]:string
}
// 此时 Man 就是
type Man = {
name: string;
age: string;
}

infer

在条件类型语句中,用 infer 声明一个变量并对他使用

type FuckType<T> = T extends (
...args: any[]
) => infer R ? R : any;

注:我也不知道这个怎么用

extends

继承,可以在声明泛型时使用,

interface Length{
long:number
}
// 表明传入的 T 要继承自 Length
function room<T extends Length>(arg:T):T{
console.log(arg)
return arg
}
room({long:99})
// 配合 extends 实现判断
// T extends U ? X :Y


索引类型

可以看做是对于上面 keyofextends 的综合应用吧

有的时候,会提取对象中的一些值,然后建立一个集合

let member = {
height:185,
phone:'16652022520',
gender:'male',
name:'硫化氢'
}
function getSpecial(person:any,keys:string[]){
return keys.map(item=> person[item])
}
const num:Array<number|string>= ['22']
// 想把 name 和 age 获取到数组里
console.log(getSpecial(member, ['name', 'age'])) // ['硫化氢',undefined]
// undefined 会获取不到值,很难受
interface Member{
height:number
phone:string
gender:"male"|'female'
name:string
}
const member:Member = {
height:185,
phone:'16652022520',
gender:'male',
name:'硫化氢'
}
// 获取的时候,让后者继承前者的 K,那后者就会被限制为只能是前者的 key
function getSpecial<T,S extends keyof T>(person:T,keys:S[]){
return keys.map(item=> person[item])
}
// 此时,member 的 key 就会被收集,限制后面传入的参数
console.log(getSpecial(member, ['name', 'age'])) // ['硫化氢',undefined]

映射类型

根据旧的类型创建出新的类型,称之为类型映射

interface Code{
name:string
label:string
fun:string
}
type OptionCode<T> ={
[p in keyof T]+?:T[p]
}
type NewCode = OptionCode<Code>
const oops:NewCode = {
label:12, // error 类型不对
cheat:'no',// error 不存在 cheat
name:'哇塞',
}
// 甚至可以在后面再添加只读属性
type ReadCode<T> = {
readonly [k in keyof T]+?=T[p]
}
type NewCode = ReadCode<Code>

在上面的示例中,我们把所有 key 都变为可选的,变为只读的,但还是有些麻烦,因为每次使用都要声明这些 type。TS 也知道这些很常用,于是直接进行封装到库里面,直接提供使用。

预设泛型

Partial

这个是 TS 已经定义的类型别名,用来将类型转化为可选类型

在 TS 中,已经定义好的是这样的

type Partial<T> = {
[P in keyof T]?: T[P];
}
// 所有类型都转换为可选类型,取消必填,取消可读,一般用来检验名称是否是正确的
interface Code{
name:string
label:string
fun:string
}
type SelectCode = Partial<Code>
// result
// 结果就是,把所有的属性改为选填

只不过因为不能穿透多层,穿透多层,自己实现下

type DeepPartial<T> ={
[p in keyof T]?: extends object? DeepPartial<T[p]>:T[p]
}

Required

同 Partial,TS 内置,的咱简单一遍过吧,表示必填。

type Required<T> = { [P in keyof T]-?: T[P]; }
// 其中 -? 表示移除 ? 这个 modifier的标识
interface Code{
name?:string
label:string
fun:string
}
type SelectCode = Partial<Code>

Readonly

同 Partial,TS 内置,被修饰的这些属性不能被重新赋值。

type Readonly<T> = {
readonly [p in keyof T]:T[p]
}

Pick

Pick<T,K> 从 T 中,选择 K 中的属性(多个键)抽出来

type Pick<T, K extends keyof T> = {
[P in K]: T[P];
}
interface Code{
name:string
label?:string
fun:string
}
type SelectCode = Pick<Code,'name'|'fun'>

Record

Record<K,V> 用于生成一个类型,或属性(对象的键)为 K,值类型为 V 的数组

// Record也是内置的属性
type Record<K extends string | number | symbol, T>={
[p in K]: T
}
// 使用 Record
interface Code{
name:string
label?:string
fun:string
}
const net:Record<'name'|'fun',Code> ={
name:{name:'civu',fun:'color'},
fun:{name:'cvc',fun:'vccb'}
}

Exclude

// 作用于 interface
// 前者不是后者的继承,如果是继承关系返回 never,如果不是,返回前者
type Exclude<T, U> = T extends U ? never : T
interface Code{
name:string
label:string
}
interface LiCode{
name:string
label:string
fun:string
}
const net:Exclude<LiCode,Code> ={
name:'cvgugu',
label:'creap'
}
type Ciial = Exclude< keyof Todo,'title'>
// Exclude 作用于联合类型时
type T0 = Exclude<"a" | "b" | "c", "a">; // "b" | "c"
type T1 = Exclude<"a" | "b" | "c", "a" | "b">; // "c"
type T2 = Exclude<string | number | (() => void), Function>; // string | number

Extract

提取,可以提取内容,

type Extract<T, U> = T extends U ? T : never
// 作用于 interface 时
interface Code {
name: string
label: string
}
interface LiCode {
name: string
label: string
fun: string
}
// 如果 T 由 U 继承而成,则返回 T 的类型
const net: Extract<LiCode, Code> = {
name: 'cvgugu',
label: 'creap',
fun: 'string'
}
// 联合类型的示例
type Minial =Extract<'12'|'22','12'|22> // '12'

Omit

// 文档中 Omit 的定义
// Omit 用于排除 T 中所有的 K 属性
type Omit<T, K extends string | number | symbol> = {
[P in Exclude<keyof T, K>]: T[P]
}
// 示例
interface Todo {
title: string;
description: string;
completed: boolean;
}
type TodoPreview = Omit<Todo, "description">;
const todo: TodoPreview = {
title: "Clean room",
completed: false,
};

NonNullable

// Lib 中对 NonNullable 的定义
type NonNullable<T> = T extends null ? never : T
// 用于清除 null ,undefined
type Clear = NonNullable<undefined|'5566'|2233|null> // "5566" | 2233

Parameters

// lib 中对 Parameters 的定义
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never
// 使用,获取当前方法的参数类型吧
type D = Parameters<typeof Math.max>; // number[]
type B = Parameters<typeof Object.keys>; // [arg: any]
type A = Parameters<() =>void>; // []
type C = Parameters<typeof parseInt>; // [string: string, radix?: number]

ReturnType

ReturnType<Fun> 获取函数(此处为 Fun)的返回值的类型

type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any

infer在这里用于提取函数类型的返回值类型。ReturnType<T> 只是将 infer R 从参数位置移动到返回值位置,因此此时 R 即是表示待推断的返回值类型。

type Func = (value:number)=>string
const fuoo:ReturnType<Func> = "1"

InstanceType

返回构造函数类型 T 的实例类型

class C {
x = 0;
y = 0;
}
type T0 = InstanceType<typeof C>; // C

ThisType

type ObjectDescriptor<D, M> = {
data?: D;
methods?: M & ThisType<D & M>; // Type of 'this' in methods is D & M
};

function makeObject<D, M>(desc: ObjectDescriptor<D, M>): D & M {
let data: object = desc.data || {};
let methods: object = desc.methods || {};
return { ...data, ...methods } as D & M;
}

let obj = makeObject({
data: { x: 0, y: 0 },
methods: {
moveBy(dx: number, dy: number) {
this.x += dx; // Strongly typed this
this.y += dy; // Strongly typed this
},
},
});

obj.x = 10;
obj.y = 20;
obj.moveBy(5, 5);

相关泛型

数组

// 如果这样定义,会导致类型推断为 string[]
const emit = ['key1', 'key2']
// 这样定义,后期无法对 keys 进行修改
const keys = ["key1", "key2"] as const; // const keys: readonly ["key1", "key2"]
type UnionToIntersection<T> = (T extends any ? (v: T) => void : never) extends (v: infer V) => void ? V : never
type LastOf<T> = UnionToIntersection<T extends any ? () => T : never> extends () => infer R ? R : never
type Push<T extends any[], V> = [ ...T, V]

type UnionToTuple<T, L = LastOf<T>, N = [T] extends [never] ? true : false> = N extends true ? [] : Push<UnionToTuple<Exclude<T, L>>, L>

declare function tuple<T extends string>(arr: T[]): UnionToTuple<T>

const c = tuple(['key1', 'key2']) // const c: ["key1", "key2"]

参考文章

作者链接
俊劫https://juejin.cn/post/6981728323051192357
Jimmy_fxhttps://juejin.cn/post/7018805943710253086